/*
 *
 */
package com.ePadink;


import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.geom.Line2D;
import java.awt.geom.Line2D.Double;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.imageio.ImageIO;

class SaveImage
{
  private byte m_penWidth;
  private Color m_signColor;
  private String m_signerName;
  private String m_SignerUID;
  private String m_signerRemarks;
  private double m_SignDate;
  private short m_signLocale;
  private short m_signWidth;
  private short m_signHeight;
  private byte m_key1;
  private byte m_key2;
  private String m_Data1;
  private byte[] m_checksum;
  private byte m_CheckSumVersion;
  private boolean m_isSigned;
  private Image backGround;
  private byte Trace_Info;
  private static final int WIDTH_IS_BYTE = 1;
  private static final byte TIME_IS_INT = 2;
  private short m_NoOfPoints;
  private Vector signPoints;
  private byte[] rawBytes;
  private int TrcSize;
  private int STSize;
  private String m_signerDesignation;
  private String m_signerOrganisation;
  private String m_signerAddress;
  private String m_signPassword;
  private String m_version;
  private String m_deviceinfo;
  private String m_Footer;
  private String m_Tag1;
  private String m_Tag2;
  private String m_Data2;
  private static final int X_IS_BYTE = 1;
  private static final int Y_IS_BYTE = 2;
  private static final int TIME_IS_WORD = 4;
  private static final int PRESSURE_IS_BYTE = 8;
  private static final int SEQNUM_IS_BYTE = 16;
  private int MaxX;
  private int MaxY;
  private int MinX;
  private int MinY;
  private int MinTime;
  private int MinPressure;
  private int MinSequenceNo;
  byte Trace_InfoEx;
  int index_extpoints;
  boolean extpoints_exist = false;
  private short m_NoOfPointsEx;
  private Vector signPointsEx;
  int index_devextpoints;
  boolean devextpoints_exist = false;
  double dev_physicalX;
  double dev_physicalY;
  private boolean isPNG = false;
  private byte signImageThickness = 2;
  private static final byte[] key = { 65, 0, 100, 0, 101, 0, 112, 0, 116, 0, 101, 0, 107, 0, 83, 0, 111, 0, 102, 0, 116, 0 };
  private boolean lastFlag;
  private TracePoint prevPoint;
  private boolean m_EnableWhiteSpaceRemoval;
  private float m_MaxEnlargementFactor;
  private boolean m_EnableMaxEnlargementFactor;
  static int newvalue;
  String m_managerUID;
  byte[] uid;

  public SaveImage()
  {
    this.m_EnableWhiteSpaceRemoval = true;
    this.m_MaxEnlargementFactor = 1.25F;
    this.m_EnableMaxEnlargementFactor = true;
  }

  private synchronized void drawSign(Graphics g, int w, int h)
  {
    Rectangle rng = new Rectangle(w, h);
    if (this.signPoints.size() > 0)
    {
      float scaleValue = 1.0F;
      int wOffset = 0; int hOffset = 0;
      float wRatio = (this.m_signWidth + 4 * this.m_penWidth) / (rng.width - 3);

      float hRatio = (this.m_signHeight + 4 * this.m_penWidth) / (rng.height - 3);

      if (hRatio > wRatio) {
        scaleValue = 1.0F / hRatio;
        wOffset = (int)(rng.width - 3 - this.m_signWidth * scaleValue) / 2;

        hOffset = (int)(2 * this.m_penWidth * scaleValue) + 4;
      } else {
        scaleValue = 1.0F / wRatio;
        hOffset = (int)(rng.height - 3 - this.m_signHeight * scaleValue) / 2;

        wOffset = (int)(2 * this.m_penWidth * scaleValue) + 4;
      }

      this.lastFlag = true;
      TracePoint tempPoint = (TracePoint)this.signPoints.elementAt(0);

      for (int i = 1; i < this.signPoints.size(); i++) {
        tempPoint = (TracePoint)this.signPoints.elementAt(i);

        putPoint(tempPoint, g, scaleValue, wOffset, hOffset);
        this.prevPoint = tempPoint;
      }
    }
    this.lastFlag = true;
  }

  private synchronized void drawSignEx(Graphics g, int w, int h)
  {
    Rectangle rng = new Rectangle(w, h);
    if (this.signPointsEx.size() > 0)
    {
      this.lastFlag = true;
      TracePoint tempPoint = (TracePoint)this.signPointsEx.elementAt(0);
      for (int i = 1; i < this.signPointsEx.size(); i++) {
        tempPoint = (TracePoint)this.signPointsEx.elementAt(i);
        if (tempPoint.mouseStatus) {
          if (this.lastFlag)
          {
            g.setColor(this.m_signColor);
            int xone = tempPoint.x * rng.width / this.MaxX;
            int yone = tempPoint.y * rng.height / this.MaxY;
            int xtwo = tempPoint.x * rng.width / this.MaxX;
            int ytwo = tempPoint.y * rng.height / this.MaxY;
            drawLine(xone, yone, xtwo, ytwo, g);
            this.lastFlag = false;
          }
          else {
            g.setColor(this.m_signColor);
            int xone = this.prevPoint.x * rng.width / this.MaxX;
            int yone = this.prevPoint.y * rng.height / this.MaxY;
            int xtwo = tempPoint.x * rng.width / this.MaxX;
            int ytwo = tempPoint.y * rng.height / this.MaxY;

            drawLine(xone, yone, xtwo, ytwo, g);
          }

        }
        else
        {
          this.lastFlag = true;
        }
        this.prevPoint = tempPoint;
      }
    }
    this.lastFlag = true;
  }

  private void drawSignEx1(Graphics g, int w, int h)
  {
    Rectangle rng = new Rectangle(w, h);

    Toolkit tk = Toolkit.getDefaultToolkit();
    int dpi = tk.getScreenResolution();

    double xLengthPx = rng.getWidth();
    double yLengthPx = rng.getHeight();
    double xLengthInInches = xLengthPx / dpi;
    double yLengthInInches = yLengthPx / dpi;

    if (!this.m_EnableWhiteSpaceRemoval) {
      if (this.signPointsEx.size() > 0)
      {
        this.lastFlag = true;
        double WidthRatio = 0.0D; double HeightRatio = 0.0D; double PCMaxXY = 0.0D;
        WidthRatio = rng.width / this.dev_physicalX;
        HeightRatio = rng.height / this.dev_physicalY;
        if (HeightRatio > WidthRatio)
          PCMaxXY = WidthRatio;
        else {
          PCMaxXY = HeightRatio;
        }
        TracePoint tempPoint = (TracePoint)this.signPointsEx.elementAt(0);
        for (int i = 1; i < this.signPointsEx.size(); i++) {
          tempPoint = (TracePoint)this.signPointsEx.elementAt(i);
          if (tempPoint.mouseStatus) {
            if (this.lastFlag)
            {
              g.setColor(this.m_signColor);
              double xone = this.dev_physicalX * tempPoint.x / this.MaxX;
              xone *= PCMaxXY;
              double yone = this.dev_physicalY * tempPoint.y / this.MaxY;
              yone *= PCMaxXY;
              double xtwo = this.dev_physicalX * tempPoint.x / this.MaxX;
              xtwo *= PCMaxXY;
              double ytwo = this.dev_physicalY * tempPoint.y / this.MaxY;
              ytwo *= PCMaxXY;
              boolean rounding = true;

              int Px1 = 0; int Py1 = 0; int Px2 = 0; int Py2 = 0;
              if (rounding) {
                if (xone >= 0.0D)
                  Px1 = (int)(xone + 0.5D);
                else {
                  Px1 = (int)(xone - 0.5D);
                }

                if (yone >= 0.0D)
                  Py1 = (int)(yone + 0.5D);
                else {
                  Py1 = (int)(yone - 0.5D);
                }
                Px2 = Px1;
                Py2 = Py1;
              } else {
                Px1 = (int)xone;
                Px2 = (int)xone;
                Py1 = (int)yone;
                Py2 = (int)yone;
              }

              drawLine(Px1, Py1, Px2, Py2, g);
              this.lastFlag = false;
            }
            else {
              g.setColor(this.m_signColor);
              double xone = this.dev_physicalX * this.prevPoint.x / this.MaxX;
              xone *= PCMaxXY;
              double yone = this.dev_physicalY * this.prevPoint.y / this.MaxY;
              yone *= PCMaxXY;
              double xtwo = this.dev_physicalX * tempPoint.x / this.MaxX;
              xtwo *= PCMaxXY;
              double ytwo = this.dev_physicalY * tempPoint.y / this.MaxY;
              ytwo *= PCMaxXY;
              boolean rounding = true;
              int Px1 = 0; int Py1 = 0; int Px2 = 0; int Py2 = 0;
              if (rounding) {
                if (xone >= 0.0D)
                  Px1 = (int)(xone + 0.5D);
                else {
                  Px1 = (int)(xone - 0.5D);
                }

                if (yone >= 0.0D)
                  Py1 = (int)(yone + 0.5D);
                else {
                  Py1 = (int)(yone - 0.5D);
                }
                if (xtwo >= 0.0D)
                  Px2 = (int)(xtwo + 0.5D);
                else {
                  Px2 = (int)(xone - 0.5D);
                }

                if (ytwo >= 0.0D)
                  Py2 = (int)(ytwo + 0.5D);
                else
                  Py2 = (int)(ytwo - 0.5D);
              }
              else {
                Px1 = (int)xone;
                Px2 = (int)xtwo;
                Py1 = (int)yone;
                Py2 = (int)ytwo;
              }

              drawLine(Px1, Py1, Px2, Py2, g);
            }

          }
          else
          {
            this.lastFlag = true;
          }
          this.prevPoint = tempPoint;
        }
      }
      this.lastFlag = true;
    } else {
      if (this.signPointsEx.size() > 0) {
        this.lastFlag = true;

        int LocalMaxX = 0; int LocalMaxY = 0; int LocalMinX = 99999; int LocalMinY = 99999;
        for (int i = 1; i < this.signPointsEx.size(); i++)
        {
          TracePoint tempPoint = (TracePoint)this.signPointsEx.elementAt(i);

          if (tempPoint.x < LocalMinX) {
            LocalMinX = tempPoint.x;
          }

          if (tempPoint.y < LocalMinY)
          {
            LocalMinY = tempPoint.y;
          }

          if (tempPoint.x > LocalMaxX) {
            LocalMaxX = tempPoint.x;
          }

          if (tempPoint.y > LocalMaxY) {
            LocalMaxY = tempPoint.y;
          }

        }

        Vector tempPoints = new Vector(this.signPointsEx.size());
        for (int i = 1; i < this.signPointsEx.size(); i++) {
          TracePoint tempPoint = (TracePoint)this.signPointsEx.elementAt(i);
          int x = tempPoint.x - LocalMinX;
          int y = tempPoint.y - LocalMinY;
          Point crrPoint = new Point(x, y);
          TracePoint currTracePoint = new TracePoint(crrPoint, tempPoint.mouseStatus, tempPoint.timer, tempPoint.Pressure, tempPoint.SequenceNo);
          tempPoints.add(currTracePoint);
        }

        int sizeX = LocalMaxX - LocalMinX + 1;
        int sizeY = LocalMaxY - LocalMinY + 1;

        double DeviceX1 = 0.0D; double DeviceY1 = 0.0D;

        DeviceX1 = sizeX * this.dev_physicalX / this.MaxX;
        DeviceY1 = sizeY * this.dev_physicalY / this.MaxY;
        double xScale = 0.0D; double yScale = 0.0D; double scale = 0.0D;
        xScale = xLengthInInches / DeviceX1;
        yScale = yLengthInInches / DeviceY1;

        scale = xScale;
        if (xScale > yScale) {
          scale = yScale;
        }

        if ((scale > 1.0D) && 
          (this.m_EnableMaxEnlargementFactor))
        {
          if (this.m_MaxEnlargementFactor > scale)
            scale = scale;
          else {
            scale = this.m_MaxEnlargementFactor;
          }
        }

        scale = 0.95D * scale;

        double xOffset = 0.0D; double yOffset = 0.0D;
        xOffset = (xLengthInInches - DeviceX1 * scale) / 2.0D * dpi;

        yOffset = (yLengthInInches - DeviceY1 * scale) / 2.0D * dpi;

        for (int i = 1; i < tempPoints.size(); i++) {
          TracePoint tempPoint = (TracePoint)tempPoints.elementAt(i);
          if (tempPoint.mouseStatus) {
            if (this.lastFlag)
            {
              g.setColor(this.m_signColor);

              double xone = tempPoint.x * DeviceX1 * scale / sizeX * dpi;
              double yone = tempPoint.y * DeviceY1 * scale / sizeY * dpi;
              double xtwo = tempPoint.x * DeviceX1 * scale / sizeX * dpi;
              double ytwo = tempPoint.y * DeviceY1 * scale / sizeY * dpi;
              xone += xOffset;
              yone += yOffset;
              xtwo += xOffset;
              ytwo += yOffset;

              boolean rounding = true;

              int Px1 = 0; int Py1 = 0; int Px2 = 0; int Py2 = 0;
              if (rounding) {
                if (xone >= 0.0D)
                  Px1 = (int)(xone + 0.5D);
                else {
                  Px1 = (int)(xone - 0.5D);
                }

                if (yone >= 0.0D)
                  Py1 = (int)(yone + 0.5D);
                else {
                  Py1 = (int)(yone - 0.5D);
                }
                Px2 = Px1;
                Py2 = Py1;
              } else {
                Px1 = (int)xone;
                Px2 = (int)xone;
                Py1 = (int)yone;
                Py2 = (int)yone;
              }

              drawLine(Px1, Py1, Px2, Py2, g);
              this.lastFlag = false;
            }
            else {
              g.setColor(this.m_signColor);
              double xone = this.prevPoint.x * DeviceX1 * scale / sizeX * dpi;
              double yone = this.prevPoint.y * DeviceY1 * scale / sizeY * dpi;
              double xtwo = tempPoint.x * DeviceX1 * scale / sizeX * dpi;
              double ytwo = tempPoint.y * DeviceY1 * scale / sizeY * dpi;
              xone += xOffset;
              yone += yOffset;
              xtwo += xOffset;
              ytwo += yOffset;
              boolean rounding = true;
              int Px1 = 0; int Py1 = 0; int Px2 = 0; int Py2 = 0;
              if (rounding) {
                if (xone >= 0.0D)
                  Px1 = (int)(xone + 0.5D);
                else {
                  Px1 = (int)(xone - 0.5D);
                }

                if (yone >= 0.0D)
                  Py1 = (int)(yone + 0.5D);
                else {
                  Py1 = (int)(yone - 0.5D);
                }
                if (xtwo >= 0.0D)
                  Px2 = (int)(xtwo + 0.5D);
                else {
                  Px2 = (int)(xone - 0.5D);
                }

                if (ytwo >= 0.0D)
                  Py2 = (int)(ytwo + 0.5D);
                else
                  Py2 = (int)(ytwo - 0.5D);
              }
              else {
                Px1 = (int)xone;
                Px2 = (int)xtwo;
                Py1 = (int)yone;
                Py2 = (int)ytwo;
              }

              drawLine(Px1, Py1, Px2, Py2, g);
            }

          }
          else
          {
            this.lastFlag = true;
          }
          this.prevPoint = tempPoint;
        }
      }

      this.lastFlag = true;
    }
  }

  private void putPoint(TracePoint thisPoint, Graphics g, float scale, int wOffset, int hOffset)
  {
    if (thisPoint.mouseStatus) {
      if (this.lastFlag)
      {
        g.setColor(this.m_signColor);
        int xone = (int)(thisPoint.x * scale) + wOffset;
        int yone = (int)(thisPoint.y * scale) + hOffset;
        int xtwo = (int)(thisPoint.x * scale) + wOffset;
        int ytwo = (int)(thisPoint.y * scale) + hOffset;

        drawLine(xone, yone, xtwo, ytwo, g);
        this.lastFlag = false;
      }
      else
      {
        g.setColor(this.m_signColor);
        int xone = (int)(this.prevPoint.x * scale) + wOffset;
        int yone = (int)(this.prevPoint.y * scale) + hOffset;
        int xtwo = (int)(thisPoint.x * scale) + wOffset;
        int ytwo = (int)(thisPoint.y * scale) + hOffset;
        drawLine(xone, yone, xtwo, ytwo, g);
      }

    }
    else
    {
      this.lastFlag = true;
    }
  }

  public byte[] getBMPImageBytes1(byte[] tempBytes, int w, int h)
  {
    try
    {
      ByteArrayInputStream bis = new ByteArrayInputStream(tempBytes);
      newvalue = bis.available();
      byte[] myarr = new byte[bis.available()];
      bis.read(myarr);
      ByteArrayInputStream bis4 = new ByteArrayInputStream(myarr);
      byte[] readarr = new byte[87];
      for (int i = 0; i <= 86; i++) {
        readarr[i] = ((byte)bis4.read());
      }
      readHeader(readarr);
      byte[] readarr1 = new byte[newvalue - 87];
      for (int i = 0; i <= newvalue - 87 - 1; i++) {
        readarr1[i] = ((byte)bis4.read());
      }
      readContent(readarr1);

      Frame f = new Frame();
      f.addNotify();
      Image im = f.createImage(w, h);
      Graphics g = im.getGraphics();
      if (this.extpoints_exist) {
        if (this.devextpoints_exist)
          drawSignEx1(g, w, h);
        else
          drawSignEx(g, w, h);
      }
      else {
        drawSign(g, w, h);
      }

      BMPWriter.write(im);
      return BMPWriter.getBMPBytes();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    return null;
  }

  public byte[] getBMPImageBytes(byte[] tempBytes, int w, int h)
  {
    try
    {
      ByteArrayInputStream bis = new ByteArrayInputStream(tempBytes);
      newvalue = bis.available();
      byte[] myarr = new byte[bis.available()];
      bis.read(myarr);
      ByteArrayInputStream bis4 = new ByteArrayInputStream(myarr);
      byte[] readarr = new byte[87];
      for (int i = 0; i <= 86; i++) {
        readarr[i] = ((byte)bis4.read());
      }
      readHeader(readarr);
      byte[] readarr1 = new byte[newvalue - 87];
      for (int i = 0; i <= newvalue - 87 - 1; i++) {
        readarr1[i] = ((byte)bis4.read());
      }
      readContent(readarr1);

      Graphics2D g = null;
      BufferedImage dstImg = null;
      IndexColorModel colorModel = null;

      int[] cmap = { -1, -16777216, -65536, -256, -16711936 };

      colorModel = new IndexColorModel(8, cmap.length, cmap, 0, true, -1, 0);

      dstImg = new BufferedImage(w, h, 12, colorModel);

      g = dstImg.createGraphics();
      g.setBackground(new Color(0, 255, 255, 255));
      this.isPNG = true;
      if (this.extpoints_exist) {
        if (this.devextpoints_exist)
          drawSignEx1(g, w, h);
        else
          drawSignEx(g, w, h);
      }
      else {
        drawSign(g, w, h);
      }

      BMPWriter.write(dstImg);
      return BMPWriter.getBMPBytes();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    return null;
  }

  public byte[] getGifImageBytes(byte[] tempBytes, int w, int h)
  {
    try
    {
      ByteArrayInputStream bis = new ByteArrayInputStream(tempBytes);
      newvalue = bis.available();
      byte[] myarr = new byte[bis.available()];
      bis.read(myarr);
      ByteArrayInputStream bis4 = new ByteArrayInputStream(myarr);
      byte[] readarr = new byte[87];
      for (int i = 0; i <= 86; i++) {
        readarr[i] = ((byte)bis4.read());
      }
      readHeader(readarr);
      byte[] readarr1 = new byte[newvalue - 87];
      for (int i = 0; i <= newvalue - 87 - 1; i++) {
        readarr1[i] = ((byte)bis4.read());
      }
      readContent(readarr1);

      Frame f = new Frame();
      f.addNotify();
      Image im = f.createImage(w, h);
      Graphics g = im.getGraphics();
      if (this.extpoints_exist) {
        if (this.devextpoints_exist)
          drawSignEx1(g, w, h);
        else
          drawSignEx(g, w, h);
      }
      else {
        drawSign(g, w, h);
      }

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      new ImageToGif(im, bos, true);
      return bos.toByteArray();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    return null;
  }

  public byte[] getJpegImageBytes(byte[] tempBytes, int w, int h, int quality)
  {
    try {
      ByteArrayInputStream bis = new ByteArrayInputStream(tempBytes);
      newvalue = bis.available();
      byte[] myarr = new byte[bis.available()];
      bis.read(myarr);
      ByteArrayInputStream bis4 = new ByteArrayInputStream(myarr);
      byte[] readarr = new byte[87];
      for (int i = 0; i <= 86; i++) {
        readarr[i] = ((byte)bis4.read());
      }
      readHeader(readarr);
      byte[] readarr1 = new byte[newvalue - 87];
      for (int i = 0; i <= newvalue - 87 - 1; i++) {
        readarr1[i] = ((byte)bis4.read());
      }
      readContent(readarr1);

      Frame f = new Frame();
      f.addNotify();
      Image im = f.createImage(w, h);
      Graphics g = im.getGraphics();
      if (this.extpoints_exist) {
        if (this.devextpoints_exist)
          drawSignEx1(g, w, h);
        else
          drawSignEx(g, w, h);
      }
      else {
        drawSign(g, w, h);
      }

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      JpegEncoder je = new JpegEncoder(im, quality, bos);
      je.Compress();
      return bos.toByteArray();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    return null;
  }

  public byte[] getPngImageBytes(byte[] tempBytes, int w, int h, boolean transparency)
  {
    setIsPNG(true);
    try {
      ByteArrayInputStream bis = new ByteArrayInputStream(tempBytes);
      newvalue = bis.available();
      byte[] myarr = new byte[bis.available()];
      bis.read(myarr);
      ByteArrayInputStream bis4 = new ByteArrayInputStream(myarr);
      byte[] readarr = new byte[87];
      for (int i = 0; i <= 86; i++) {
        readarr[i] = ((byte)bis4.read());
      }
      readHeader(readarr);
      byte[] readarr1 = new byte[newvalue - 87];
      for (int i = 0; i <= newvalue - 87 - 1; i++) {
        readarr1[i] = ((byte)bis4.read());
      }
      readContent(readarr1);

      Graphics2D twog = null;
      BufferedImage image = null;
      IndexColorModel colorModel = null;
      if (transparency) {
        int[] cmap = { 0, -16777216, -65536, -256, -16711936 };

        colorModel = new IndexColorModel(8, cmap.length, cmap, 0, true, -1, 0);

        image = new BufferedImage(w, h, 13, colorModel);

        twog = image.createGraphics();
        twog.setBackground(new Color(1, 255, 255, 255));
      }
      else {
        int[] cmap = { -1, -16777216, -65536, -256, -16711936 };

        colorModel = new IndexColorModel(8, cmap.length, cmap, 0, true, -1, 0);

        image = new BufferedImage(w, h, 13, colorModel);

        twog = image.createGraphics();
        twog.setBackground(new Color(0, 255, 255, 255));
      }

      if (this.extpoints_exist) {
        if (this.devextpoints_exist)
          drawSignEx1(twog, w, h);
        else
          drawSignEx(twog, w, h);
      }
      else {
        drawSign(twog, w, h);
      }

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ImageIO.write(image, "PNG", bos);
      return bos.toByteArray();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
    return null;
  }

  private byte[] convertToByte(String str)
  {
    byte[] thisArray = new byte[str.length() / 2];
    int lbit = 0; int hbit = 0;
    int i = 0; for (int Index = 0; i <= str.length() - 1; Index++) {
      lbit = Character.digit(str.charAt(i), 16);
      i++;
      if (i == str.length()) {
        break;
      }
      hbit = Character.digit(str.charAt(i), 16);
      thisArray[Index] = ((byte)(lbit * 16 + hbit));

      i++;
    }

    return thisArray;
  }

  private void readHeader(byte[] HeaderBytes)
  {
    this.m_penWidth = HeaderBytes[0];

    int colorInt = HeaderBytes[3] & 0xFF | (HeaderBytes[2] & 0xFF) << 8 | (HeaderBytes[1] & 0xFF) << 16;

    this.m_signColor = new Color(colorInt);

    this.m_signLocale = ((short)(HeaderBytes[5] & 0xFF));
    int tempInt = HeaderBytes[6] & 0xFF;
    if (tempInt > 128)
      tempInt = -(this.m_signLocale + (tempInt - 128 << 8));
    else {
      tempInt = this.m_signLocale + (tempInt << 8);
    }
    this.m_signLocale = ((short)tempInt);
    this.m_signWidth = ((short)(HeaderBytes[7] & 0xFF | (HeaderBytes[8] & 0xFF) << 8));

    this.m_signHeight = ((short)(HeaderBytes[9] & 0xFF));
    StringBuffer tempStr = new StringBuffer(36);
    for (int i = 0; i < 18; i++) {
      char c = Character.forDigit(HeaderBytes[(10 + i)] & 0xF, 16);
      tempStr.append(c);
      c = Character.forDigit(HeaderBytes[(10 + i)] & 0xF0, 16);
      tempStr.append(c);
    }
    this.m_SignerUID = tempStr.toString();

    long tempLong = 0L;
    for (int i = 65; i < 73; i++) {
      tempLong |= (HeaderBytes[i] & 0xFF) << (i - 65) * 8;
    }
    
    Long lTemp = new Long(tempLong);
    this.m_SignDate = lTemp.doubleValue();
    
    this.STSize = (HeaderBytes[82] & 0xFF | (HeaderBytes[83] & 0xFF) << 8 | (HeaderBytes[84] & 0xFF) << 16 | (HeaderBytes[85] & 0xFF) << 24);

    this.Trace_Info = HeaderBytes[86];
  }

  private void readContent(byte[] ContentArray)
  {
    int strcopy = 0;
    char[] mychar = new char[10];
    if (this.STSize > 0) {
      byte[] m_SignTrace = new byte[this.STSize + 1];
      strcopy = newvalue - 87 - this.STSize - 1 - 1;
      System.arraycopy(ContentArray, 0, m_SignTrace, 0, this.STSize + 1);
      int curPos = this.STSize + 1;
      if (ContentArray[curPos] != 0) {
        this.m_managerUID = readUID(ContentArray, curPos);
        curPos += 16;
        strcopy = newvalue - 87 - this.STSize - 1 - 17;
      }
      byte[] StringBytes = new byte[strcopy];

      String wholeString = dumpStringBytes(StringBytes, ContentArray, curPos + 1, 0, strcopy);

      this.index_extpoints = wholeString.indexOf("EXTPOINTS");
      this.extpoints_exist = false;
      if (this.index_extpoints >= 0) {
        this.extpoints_exist = true;
      }

      StringTokenizer st = new StringTokenizer(wholeString, "\b");
      this.m_signerName = st.nextToken();
      this.m_signerName = extractString(this.m_signerName);

      this.m_signerDesignation = st.nextToken();
      this.m_signerDesignation = this.m_signerDesignation.substring(1);
      this.m_signerDesignation = extractString(this.m_signerDesignation);

      this.m_signerOrganisation = st.nextToken();
      this.m_signerOrganisation = this.m_signerOrganisation.substring(1);
      this.m_signerOrganisation = extractString(this.m_signerOrganisation);

      this.m_signerAddress = st.nextToken();
      this.m_signerAddress = this.m_signerAddress.substring(1);
      this.m_signerAddress = extractString(this.m_signerAddress);

      this.m_signerRemarks = st.nextToken();
      this.m_signerRemarks = this.m_signerRemarks.substring(1);
      this.m_signerRemarks = extractString(this.m_signerRemarks);

      this.m_signPassword = st.nextToken();
      this.m_signPassword = this.m_signPassword.substring(1);
      this.m_signPassword = extractString(this.m_signPassword);

      this.m_version = st.nextToken();
      this.m_version = this.m_version.substring(1);
      this.m_version = extractString(this.m_version);

      StringTokenizer st1 = new StringTokenizer(this.m_version, "~");
      this.m_version = st1.nextToken();
      this.m_deviceinfo = st1.nextToken();

      if (this.extpoints_exist)
      {
        int start_index = curPos + this.index_extpoints + 9 + 1;
        int signtraceEx_len = ContentArray.length - start_index - 1;
        byte[] m_SignTraceEx = new byte[signtraceEx_len];
        System.arraycopy(ContentArray, start_index + 1, m_SignTraceEx, 0, signtraceEx_len);

        bytesToArrayEx(m_SignTraceEx);
      }
      else {
        bytesToArray(m_SignTrace);
      }
    }
  }

  private String readUID(byte[] thisArray, int Index)
  {
    this.uid = new byte[16];
    char[] mychar = new char[2];
    int uByte = 0; int firstHex = 0; int secondHex = 0;
    String wholeStr = "";
    String str = "";
    for (int i = 0; i <= 15; i++) {
      this.uid[i] = thisArray[Index];
      uByte = thisArray[Index];
      uByte = 128 + uByte;
      firstHex = uByte % 16;
      secondHex = uByte / 16;
      mychar[0] = Character.forDigit(firstHex, 16);
      mychar[1] = Character.forDigit(secondHex, 16);
      str = new String(mychar);
      wholeStr = wholeStr + str;
      Index++;
    }
    return wholeStr;
  }

  private String extractString(String s) {
    char[] mychar = new char[s.length() / 2];
    int l = 0; int m = 0; int n = 0;
    int i = 0; for (int p = 0; i <= s.length() - 1; p++) {
      l = s.charAt(i);
      i++;
      m = s.charAt(i);
      n = l + m * 256;
      mychar[p] = ((char)n);

      i++;
    }

    return new String(mychar);
  }

  private String dumpStringBytes(byte[] dest, byte[] source, int SOffset, int dOffset, int CopyLength)
  {
    int i = 0;
    char[] myChar = new char[CopyLength];
    for (i = 0; i <= CopyLength - 1; i++) {
      dest[(i + dOffset)] = source[(i + SOffset)];
      myChar[i] = ((char)dest[(i + dOffset)]);
    }
    return new String(myChar);
  }

  private void bytesToArray(byte[] TraceBytes)
  {
    int TPASize = 0;
    int UDTSize = 1;
    if ((this.Trace_Info & 0x1) != 0)
      UDTSize += 1;
    else {
      UDTSize += 2;
    }
    if ((this.Trace_Info & 0x2) != 0)
      UDTSize += 2;
    else {
      UDTSize += 4;
    }
    TPASize = (this.STSize + 1) / UDTSize;
    this.m_NoOfPoints = ((short)TPASize);
    this.signPoints = new Vector(this.m_NoOfPoints);
    int k = 0; int byteIndex = 0;
    for (k = 0; k <= this.m_NoOfPoints - 1; k++) {
      TracePoint tp = new TracePoint();
      if ((this.Trace_Info & 0x1) != 0) {
        tp.x = (TraceBytes[byteIndex] & 0xFF);
        byteIndex++;
      } else {
        tp.x = (TraceBytes[byteIndex] & 0xFF | (TraceBytes[(byteIndex + 1)] & 0xFF) << 8);

        byteIndex += 2;
      }
      tp.y = (TraceBytes[byteIndex] & 0xFF);
      byteIndex++;
      if ((this.Trace_Info & 0x2) != 0) {
        tp.timer = (TraceBytes[byteIndex] & 0xFF | (TraceBytes[(byteIndex + 1)] & 0xFF) - 127 << 8);

        tp.mouseStatus = ((TraceBytes[(byteIndex + 1)] & 0xFF) < 128);
        byteIndex += 2;
      } else {
        tp.timer = (TraceBytes[byteIndex] & 0xFF | (TraceBytes[(byteIndex + 1)] & 0xFF) << 8 | (TraceBytes[(byteIndex + 2)] & 0xFF) << 16 | (TraceBytes[(byteIndex + 3)] & 0xFF) - 127 << 24);

        tp.mouseStatus = ((TraceBytes[(byteIndex + 3)] & 0xFF) < 128);
        byteIndex += 4;
      }
      this.m_signWidth = ((short)Math.max(this.m_signWidth, tp.x));
      this.m_signHeight = ((short)Math.max(this.m_signHeight, tp.y));
      this.signPoints.addElement(tp);
    }
    this.m_isSigned = true;
  }

  private void bytesToArrayEx(byte[] signtrace_ex)
  {
    int TPASize = 0;
    int UDTSize = 0;
    int byteIndex = 0;

    int datalen = signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24;

    byteIndex += 4;
    byte[] tempbytes = new byte[signtrace_ex.length - datalen - 4];
    String CompleteString = dumpStringBytes(tempbytes, signtrace_ex, datalen + 4, 0, signtrace_ex.length - datalen - 4);

    this.index_devextpoints = CompleteString.indexOf("DEVEXTENTS");
    this.devextpoints_exist = false;
    if (this.index_devextpoints >= 0) {
      this.devextpoints_exist = true;
    }

    this.MaxX = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 4;
    this.MaxY = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 8;
    this.MinX = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 4;
    this.MinY = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 4;
    this.MinTime = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 4;
    this.MinPressure = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 4;
    this.MinSequenceNo = (signtrace_ex[byteIndex] & 0xFF | (signtrace_ex[(byteIndex + 1)] & 0xFF) << 8 | (signtrace_ex[(byteIndex + 2)] & 0xFF) << 16 | (signtrace_ex[(byteIndex + 3)] & 0xFF) << 24);

    byteIndex += 4;
    this.Trace_InfoEx = signtrace_ex[byteIndex];
    datalen -= 37;
    byte[] Sign_Trace = new byte[datalen];
    System.arraycopy(signtrace_ex, 37, Sign_Trace, 0, datalen);

    if ((this.Trace_InfoEx & 0x1) != 0)
      UDTSize += 1;
    else {
      UDTSize += 2;
    }
    if ((this.Trace_InfoEx & 0x2) != 0)
      UDTSize += 1;
    else {
      UDTSize += 2;
    }

    if ((this.Trace_InfoEx & 0x4) != 0)
      UDTSize += 2;
    else {
      UDTSize += 4;
    }

    if ((this.Trace_InfoEx & 0x8) != 0)
      UDTSize += 1;
    else {
      UDTSize += 4;
    }

    if ((this.Trace_InfoEx & 0x10) != 0)
      UDTSize += 1;
    else {
      UDTSize += 4;
    }

    TPASize = (Sign_Trace.length + 1) / UDTSize;
    this.m_NoOfPointsEx = ((short)TPASize);
    this.signPointsEx = new Vector(this.m_NoOfPoints);
    int k = 0;
    byteIndex = 0;
    for (k = 0; k <= this.m_NoOfPointsEx - 1; k++) {
      TracePoint tpEx = new TracePoint();
      if ((this.Trace_InfoEx & 0x1) != 0)
      {
        tpEx.x = (this.MinX + (Sign_Trace[byteIndex] & 0xFF));
        byteIndex += 1;
      }
      else {
        tpEx.x = (this.MinX + (Sign_Trace[byteIndex] & 0xFF | (Sign_Trace[(byteIndex + 1)] & 0xFF) << 8));

        byteIndex += 2;
      }

      if ((this.Trace_InfoEx & 0x2) != 0)
      {
        tpEx.y = (this.MinY + (Sign_Trace[byteIndex] & 0xFF));
        byteIndex += 1;
      }
      else {
        tpEx.y = (this.MinY + (Sign_Trace[byteIndex] & 0xFF | (Sign_Trace[(byteIndex + 1)] & 0xFF) << 8));

        byteIndex += 2;
      }

      if ((this.Trace_InfoEx & 0x4) != 0)
      {
        tpEx.timer = (this.MinTime + (Sign_Trace[byteIndex] & 0xFF | (Sign_Trace[(byteIndex + 1)] & 0xFF) - 127 << 8));

        tpEx.mouseStatus = ((Sign_Trace[(byteIndex + 1)] & 0xFF) < 128);
        byteIndex += 2;
      }
      else
      {
        tpEx.timer = (this.MinTime + (Sign_Trace[byteIndex] & 0xFF | (Sign_Trace[(byteIndex + 1)] & 0xFF) << 8 | (Sign_Trace[(byteIndex + 2)] & 0xFF) << 16 | (Sign_Trace[(byteIndex + 3)] & 0xFF) - 127 << 24));

        tpEx.mouseStatus = ((Sign_Trace[(byteIndex + 3)] & 0xFF) < 128);
        byteIndex += 4;
      }

      if ((this.Trace_InfoEx & 0x8) != 0)
      {
        tpEx.Pressure = (this.MinPressure + (Sign_Trace[byteIndex] & 0xFF));
        byteIndex++;
      }
      else {
        tpEx.Pressure = (this.MinPressure + (Sign_Trace[byteIndex] & 0xFF | (Sign_Trace[(byteIndex + 1)] & 0xFF) << 8 | (Sign_Trace[(byteIndex + 2)] & 0xFF) << 16 | (Sign_Trace[(byteIndex + 3)] & 0xFF) << 24));

        byteIndex += 4;
      }

      if ((this.Trace_InfoEx & 0x10) != 0)
      {
        tpEx.SequenceNo = (this.MinSequenceNo + (Sign_Trace[byteIndex] & 0xFF));
        byteIndex++;
      }
      else
      {
        tpEx.SequenceNo = (this.MinSequenceNo + (Sign_Trace[byteIndex] & 0xFF | (Sign_Trace[(byteIndex + 1)] & 0xFF) << 8 | (Sign_Trace[(byteIndex + 2)] & 0xFF) << 16 | (Sign_Trace[(byteIndex + 3)] & 0xFF) << 24));

        byteIndex += 4;
      }
      this.signPointsEx.addElement(tpEx);
    }
    if (this.devextpoints_exist)
    {
      readDeviceExtents(tempbytes);
    }

    this.m_isSigned = true;
  }

  private void readDeviceExtents(byte[] devextents)
  {
    int byteIndex = 11;
    int datalen = devextents[byteIndex] & 0xFF | (devextents[(byteIndex + 1)] & 0xFF) << 8 | (devextents[(byteIndex + 2)] & 0xFF) << 16 | (devextents[(byteIndex + 3)] & 0xFF) << 24;

    byteIndex += 4;
    datalen = 16;
    byte[] dataBytes = new byte[datalen];
    System.arraycopy(devextents, byteIndex, dataBytes, 0, datalen);
    long tempLongx = 0L;
    long tempLongy = 0L;
    for (int i = 0; i < 8; i++) {
      tempLongx |= (dataBytes[i] & 0xFF) << (i - 0) * 8;

      tempLongy |= (dataBytes[(i + 8)] & 0xFF) << (i - 0) * 8;
    }
    
    Long lTempX = new Long(tempLongx);
    this.dev_physicalX = lTempX.doubleValue();
    
    Long lTempY = new Long(tempLongx);
    this.dev_physicalY = lTempY.doubleValue();
  }

  private void drawLine(int x1, int y1, int x2, int y2, Graphics g)
  {
    if (this.isPNG) {
      Graphics2D g2d = (Graphics2D)g;
      Stroke stroke = new BasicStroke(this.signImageThickness, 1, 1);

      g2d.setStroke(stroke);

      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

      Line2D line = new Line2D.Double(x1, y1, x2, y2);

      g2d.draw(line);
    }
    else {
      Graphics2D g2d = (Graphics2D)g;
      Stroke stroke = new BasicStroke(this.signImageThickness, 1, 1);

      g2d.setStroke(stroke);

      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

      Line2D line = new Line2D.Double(x1, y1, x2, y2);

      g2d.draw(line);
    }
  }

  private void setIsPNG(boolean isPNG)
  {
    this.isPNG = isPNG;
  }

  public void setSignImageThickness(byte signImageThickness) {
    this.signImageThickness = signImageThickness;
  }
  public void enableWhiteSpaceRemoval(boolean state) {
    this.m_EnableWhiteSpaceRemoval = state;
  }

  public void setMaxEnlargementFactor(float value)
  {
    if ((value < 1.0F) || (value > 2.0F))
      this.m_MaxEnlargementFactor = 1.25F;
    else
      this.m_MaxEnlargementFactor = value;
  }

  public void enableMaxEnlargementFactor(boolean value)
  {
    this.m_EnableMaxEnlargementFactor = value;
  }
}